package org.demo.rc.client;

import java.io.ByteArrayOutputStream;
import java.io.InputStream;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
import java.net.SocketException;
import java.net.URL;
import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.Queue;
import java.util.concurrent.ConcurrentLinkedQueue;

import org.demo.rc.UDPImageMessage;

/**
 * 远程控制：客户端
 * @author  
 * @date    2010-10-22
 * @file    org.demo.rc.MonitorClient.java
 */
public class MonitorClient {
	// 
	private boolean closed = false;
	private Properties properties = null;
	// 
	private DatagramSocket cmd_socket = null;
	private DatagramSocket data_socket = null;
	private Thread data_task = null;
	private Queue<byte[]> img_queue = null;
	//
	private String current_host = null;
	private Map<String,Integer> port_map = new HashMap<String, Integer>();
	// 
	/**
	 * 构造函数
	 * @throws SocketException 
	 */
	public MonitorClient() throws SocketException{
		// load config
		URL url = this.getClass().getResource("client.properties");
		InputStream in = null;
		try {
			in = url.openStream();
			properties = new Properties();
			properties.load(in);
		} catch (Exception e){
			System.out.println("init:> " + e.getMessage());
		} finally {
			try { if (in != null)in.close(); } catch (Exception e){}
		}
		// 
		cmd_socket = new DatagramSocket();
		data_socket = new DatagramSocket();
		data_task = new Thread(new DataTask(this));
		img_queue = new ConcurrentLinkedQueue<byte[]>();
		// 
		data_task.start();
	}
	/**
	 * 获取配置信息
	 * @return
	 */
	public Properties getProperties(){
		return this.properties;
	}
	/**
	 * 连接到指定 server
	 * @param ip
	 * @param port
	 */
	public void connect(String ip,int port){
		port_map.put(ip, port);
		current_host = ip;
		int data_port = data_socket.getLocalPort();
		int data_buf_size = Integer.parseInt(
                                    properties.getProperty("data_buf_size", "50")) * 1024;
		sendCommand("connect;" + data_port);
		sendCommand("data_buf_size;" + data_buf_size);
	}
	/**
	 * 断开当前连接
	 */
	public void disconnect(){
		sendCommand("disconnect");
		port_map.remove(current_host);
		current_host = null;
	}
	/**
	 * 向控制端发送命令
	 * @param cmd
	 */
	public void sendCommand(String cmd){
		if (cmd == null || current_host == null){
			return;
		}
		try {
			String host = current_host;
			Integer port = port_map.get(host);
			if (host == null || port == null){
				return;
			}
			DatagramPacket packet = createDataPack(host, port, cmd.getBytes());
			cmd_socket.send(packet);
		} catch (Exception e){
			System.out.println("send command:> " + e.getMessage());
		}
	}
	/**
	 * 创建发送数据包
	 * @param host
	 * @param port
	 * @param data
	 * @return
	 */
	private DatagramPacket createDataPack(String host,int port,byte[] buf){
		try {
			InetAddress address = InetAddress.getByName(host);
			return new DatagramPacket(buf,buf.length,address,port);
		} catch(Exception e){
			throw new RuntimeException(e);
		}
	}
	/**
	 * 关闭程序
	 */
	public void close(){
		this.closed = true;
		cmd_socket.close();
		data_socket.close();
	}
	/**
	 * 判断是否已关闭程序
	 * @return
	 */
	public boolean isClosed(){
		return this.closed;
	}
	/**
	 * 获取当前被控端 ip
	 * @return
	 */
	public String getCurrentHost(){
		return this.current_host;
	}			
	/**
	 * 获取数据通道
	 * @return
	 */
	private DatagramSocket getDataSocket(){
		return this.data_socket;
	}
	/**
	 * 获取缓存的图像队列
	 * @return
	 */
	public Queue<byte[]> getImgQueue(){
		return this.img_queue;
	}	
	/**
	 * 数据任务类
	 * @author 
	 *
	 */
	private class DataTask implements Runnable{
		private MonitorClient parent = null;
		// 数据块大小 50k
		private int data_buf_size = 51200;
		// 超时时间 3秒
		private int cache_timeout = 3000;
		// 保存图像的最大个数
		private int CACHE_MAX_SIZE = 10;
		// 图像缓存
		private Map<String,List<UDPImageMessage>> cacheMap = new HashMap<String,List<UDPImageMessage>>(CACHE_MAX_SIZE);
		/**
		 * 构造函数
		 * @param parent
		 */
		public DataTask(MonitorClient parent){
			this.parent = parent;
			data_buf_size = Integer.parseInt(
					                parent.getProperties()
					               .getProperty("data_buf_size", "50")) * 1024;
			cache_timeout = Integer.parseInt(
					                parent.getProperties()
					               .getProperty("cache_timeout", "3000"));
		}
		/**
		 * 执行任务
		 */
		public void run() {
			byte[] buf = new byte[data_buf_size];
			DatagramPacket packet = null;
			UDPImageMessage tmp = null;
			UDPImageMessage message = null;
			List<UDPImageMessage> list = null;
			while (!parent.isClosed()){
				try {
					packet = new DatagramPacket(buf, buf.length);
					parent.getDataSocket().receive(packet);
					message = new UDPImageMessage();
					message.unpack(packet.getData(), packet.getOffset(), packet.getLength());
					list = cacheMap.get(message.getMd5());
					if (list == null){
						list = new LinkedList<UDPImageMessage>();
						list.add(message);
						cacheMap.put(message.getMd5(), list);
						if (cacheMap.size() >= CACHE_MAX_SIZE){
							Iterator<String> it = cacheMap.keySet().iterator();
							while(it.hasNext()){
								tmp = cacheMap.get(it.next()).get(0);
								if ((System.currentTimeMillis() - tmp.getTime()) > cache_timeout){									
									it.remove();
								}
							}
						}						
					} else {
						// list 中的数据块按 index 进行排序
						int listSize = list.size();
						for (int i=0; i<listSize; i++){
							tmp = list.get(i);
							if (message.getIndex() < tmp.getIndex()){
								list.add(i, message);
								break;
							} 
							if ((i+1) == listSize){
								list.add(message);
								break;
							}
						}						
					}
					if (list.size() == message.getSize()){							
						cacheMap.remove(message.getMd5());
						ByteArrayOutputStream out = new ByteArrayOutputStream();
						for (int i=0; i<list.size(); i++){
							out.write(list.get(i).getData());
						}
						out.flush();							
						parent.getImgQueue().offer(out.toByteArray());
					}
				} catch(Exception e){
					System.out.println("receive data:> " + e.getMessage());
				}
			}
		}
	}
}
